import { deepMerge } from '../function/object'
import { queryParams } from '../function/params'
import validate from '../function/validate'
import config from '@/config/index'
import Interceptor from './interceptor'
import { cacheType } from '@/config'
import { CACHE } from '@/config/constant'
import { setCache, getCache } from '@/utils/uni/cache'

class Request {
  // 设置全局默认配置
  setConfig(customConfig) {
    // 深度合并对象，否则会造成对象深层属性丢失
    this.config = deepMerge(this.config, customConfig)
  }

  // 主要请求部分
  async request(options = {}) {
    options = deepMerge(this.config, options)
    options.params = options.params || {}
    options.header = Object.assign(this.config.header, options.header)

    // 判断用户传递的URL是否/开头,如果不是,加上/，这里使用了验证库的url()方法
    options.url = validate.url(options.url)
      ? options.url
      : options.baseUrl + (options.url.indexOf('/') == 0 ? options.url : '/' + options.url)

    // 处理GET请求的params参数，将其转换为URL查询字符串
    if (options.method === 'GET' && options.params && Object.keys(options.params).length > 0) {
      const queryString = queryParams(options.params, !options.url.includes('?'))
      options.url += queryString
      // 清除params，避免uni.request报错
      delete options.params
    }

    // 网络请求缓存检查
    if ((cacheType != CACHE.NOCACHE && !options.noCache) || options.cache) {
      let cache = true
      if (cacheType == CACHE.NETWORKCACHE) {
        uni.getNetworkType({
          success: await function (res) {
            if (res.networkType != 'unknown' && res.networkType != 'none') {
              cache = false
            }
          },
        })
      }
      const apiData = getCache(options.url)
      if (apiData && cache) {
        return Promise.resolve(apiData)
      }
    }

    // 检查请求拦截
    if (this.interceptor.request && typeof this.interceptor.request === 'function') {
      let interceptorRequest = this.interceptor.request(options)
      if (interceptorRequest === false) {
        // 返回一个处于pending状态中的Promise，来取消原promise，避免进入then()回调
        return new Promise(() => {})
      }
    }

    return new Promise((resolve, reject) => {
      options.complete = (response) => {
        // 请求返回后，隐藏loading(如果请求返回快的话，可能会没有loading)
        uni.hideLoading()
        // 清除定时器，如果请求回来了，就无需loading
        clearTimeout(this.config.timer)
        this.config.timer = null
        // 判断用户对拦截返回数据的要求，如果originalData为true，返回所有的数据(response)到拦截器，否则只返回response.data
        if (this.config.originalData) {
          // 判断是否存在拦截器
          if (this.interceptor.response && typeof this.interceptor.response === 'function') {
            let resInterceptors = this.interceptor.response(response, options.handleError)
            // 如果拦截器不返回false，就将拦截器返回的内容给this.$api.post的then回调
            if (resInterceptors !== false) {
              resolve(resInterceptors)
            } else {
              // 拦截器返回false时，直接返回pending状态的Promise，避免触发catch显示通用错误
              return new Promise(() => {})
            }
          } else {
            // 如果要求返回原始数据，就算没有拦截器，也返回最原始的数据
            resolve(response)
          }
        } else {
          if (response.statusCode == 200) {
            // 网络缓存 - 只有成功响应才缓存
            if (response.data?.success && ((cacheType != CACHE.NOCACHE && !options.noCache) || options.cache)) {
              setCache(options.url, response.data)
            }
            if (this.interceptor.response && typeof this.interceptor.response === 'function') {
              let resInterceptors = this.interceptor.response(response.data, options.handleError)
              if (resInterceptors !== false) {
                resolve(resInterceptors)
              } else {
                // 拦截器返回false时，直接返回pending状态的Promise，避免触发catch显示通用错误
                return new Promise(() => {})
              }
            } else {
              // 如果不是返回原始数据(originalData=false)，且没有拦截器的情况下，返回纯数据给then回调
              resolve(response.data)
            }
          } else {
            // 不返回原始数据的情况下，服务器状态码不为200，response.errMsg错误提示
            if (options.handleError) {
              uni.showToast({
                title: this.$t('utils_request_index_msg_9c5a1d67'),
                icon: 'none',
                mask: true,
              })
            }
            reject(response)
          }
        }
      }

      // 是否显示loading
      // 加一个是否已有timer定时器的判断，否则有两个同时请求的时候，后者会清除前者的定时器id
      // 而没有清除前者的定时器，导致前者超时，一直显示loading
      if (options.showLoading && !this.config.timer) {
        this.config.timer = setTimeout(() => {
          uni.showLoading({
            title: this.config.loadingText,
            mask: this.config.loadingMask,
          })
          this.config.timer = null
        }, this.config.loadingTime)
      }
      uni.request(options)
    }).catch((res) => {
      uni.showToast({
        title: '网络异常，请稍后再试',
        icon: 'none',
        mask: true,
      })
      // 错误响应不应该被缓存
      // 如果返回reject()，不让其进入this.$api.post().then().catch()后面的catch()
      // 因为很多人都会忘了写后面的catch()，导致报错捕获不到catch
      return new Promise(() => {})
    })
  }

  constructor() {
    this.config = {
      baseUrl: config.BASE_URL, // 请求的根域名
      // 默认的请求头
      header: {},
      method: 'POST',
      // 设置为json，返回后uni.request会对数据进行一次JSON.parse
      dataType: 'json',
      // 此参数无需处理，因为5+和支付宝小程序不支持，默认为text即可
      responseType: 'text',
      showLoading: true, // 是否显示请求中的loading
      loadingText: '正在加载中...',
      loadingTime: 800, // 在此时间内，请求还没回来的话，就显示加载中动画，单位ms
      timer: null, // 定时器
      originalData: false, // 是否在拦截器中返回服务端的原始数据，见文档说明
      loadingMask: true, // 展示loading的时候，是否给一个透明的蒙层，防止触摸穿透
      handleError: true, // 是否处理请求失败情况
    }

    // 拦截器
    this.interceptor = {
      // 请求前的拦截
      request: Interceptor.request,
      // 请求后的拦截
      response: Interceptor.response,
    }
  }
}
export default new Request()
